/* shmem.c : dgnsc dynamic shared memory
 * Copyright (C) 2009-2010 drangon <drangon.zhou@gmail.com>
 * 2009-05
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <dgnsc/shmem.h>
#include <dgnsc/common.h>

#ifdef _WIN32
#include <windows.h>
#else
#include <stdlib.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <fcntl.h>    
#include <unistd.h>
#define HANDLE int
#endif

struct dgn_shmem_st
{
	HANDLE shm_fd;
	char * base_addr;
};

#define DGN_SHMEM_MAGIC		0xFEEDBEEF

// NOTE : alloc more 16byte, | 0 | magic | headlen = 16 | datalen |

dgn_shmem_t dgn_shmem_init( const char * name, int size )
{
	struct dgn_shmem_st * shm = (struct dgn_shmem_st *)malloc( sizeof(struct dgn_shmem_st) );
	int is_create = 0;

#ifdef _WIN32
	shm->shm_fd = CreateFileMapping( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, size + 16, name );
	if( shm->shm_fd == NULL ) {
		free( shm ), shm = NULL;
		return NULL;
	}
	if( GetLastError() != ERROR_ALREADY_EXISTS )
		is_create = 1;

	shm->base_addr = (char *)MapViewOfFile( shm->shm_fd, FILE_MAP_WRITE, 0, 0, size + 16 );
	if( shm->base_addr == NULL ) {
		free( shm ), shm = NULL;
		return NULL;
	}

#else
	shm->shm_fd = shm_open( name, O_RDWR, 0664 );
	if( shm->shm_fd < 0 ) {
		shm->shm_fd = shm_open( name, O_RDWR | O_CREAT, 0664 );
		if( shm->shm_fd < 0 ) {
			free( shm ), shm = NULL;
			return NULL;
		}
		is_create = 1;
		ftruncate( shm->shm_fd, size + 16 );
	}
	shm->base_addr = mmap( NULL, size + 16, PROT_WRITE, MAP_SHARED, shm->shm_fd, 0 );
	if( shm->base_addr == MAP_FAILED ) {
		free( shm ), shm = NULL;
		return NULL;
	}
		
#endif

	if( is_create ) {
		// create, init first 16 byte
		int32_t * p = (int *)shm->base_addr;
		p[0] = 0;
		p[1] = DGN_SHMEM_MAGIC;
		p[2] = 16;
		p[3] = size;
	}

	return shm;
}

int dgn_shmem_get( dgn_shmem_t shm, char ** ret_mem, int * ret_size )
{
	if( shm == NULL ) {
		PR_DEBUG( "shm is null" );
		*ret_mem = NULL;
		*ret_size = 0;
		return -1;
	}
	*ret_mem = shm->base_addr + 16;
	*ret_size = ((int32_t *)shm->base_addr)[3];
	return 0;
}

int dgn_shmem_fini( dgn_shmem_t shm )
{
	if( shm == NULL )
		return 0;

#ifdef _WIN32
	UnmapViewOfFile( shm->base_addr );
	CloseHandle( shm->shm_fd );
#else
	int size = ((int32_t *)shm->base_addr)[3];
	munmap( shm->base_addr, size );
	close( shm->shm_fd );
#endif

	free( shm ), shm = NULL;
	return 0;
}

